/*  
	This file is a part of AI game assignment.
	
	Author : Satya Gautham Merla

*/
#include <fstream>
#include <cstdlib>
#include "game.h"
#include "g_1.cpp"
#include "g_2.cpp"
using namespace std;
//----------------------------------------------------
//	purpose: initialize the board and other private varialbles
//	
//	input: none
//	ouput: none
//----------------------------------------------------
void Game::initialize()
{
	for ( int i=0; i<ROW_SIZE; i++)
	{
		for (int j=0; j<COL_SIZE; j++)
		{
			board[i][j] = EMPTY;
		}
	}

	board[ROW_SIZE/2 -1][COL_SIZE/2 -1] = BLUE;
	board[ROW_SIZE/2][COL_SIZE/2] = BLUE;
	board[ROW_SIZE/2 -1][COL_SIZE/2] = RED;
	board[ROW_SIZE/2 ][COL_SIZE/2 -1] = RED;


	timeLimit = 1000; 	// 1 sec
	
	scoreOne = 0 ;
	scoreTwo = 0 ;
	
	pointsOne = 0;
	pointsTwo = 0;
}

//----------------------------------------------------
//	purpose: print the board 
//	
//	input: none
//	ouput: none
//----------------------------------------------------

void Game::printBoard()
{
	cout <<" ";
	for (int j=0; j<COL_SIZE; j++)
	{
		cout << "  "<<j;
	}
	cout <<"\n";
	for ( int i=0; i<ROW_SIZE; i++)
	{
		cout << i;

		for (int j=0; j<COL_SIZE; j++)
		{
			cout << "  "<<board[i][j];
		}
		cout << "\n";
	}
}
//----------------------------------------------------
//	purpose: run the game
//	
//	input: none
//	ouput: none
//----------------------------------------------------

void Game::run()
{
	initialize();

	// round One
	colOfPlayer[0] = BLUE;	//assign colors to players
	colOfPlayer[1] = RED;
	
	while(1)
	{
		
		struct move * presentMove;
		presentMove = (move *) malloc (sizeof (struct move));

		presentMove->x_pos = -2; 
		presentMove->y_pos = -2;	//initialize the move 

		tempTimeLimit = timeLimit;
	
		for ( int i=0; i<ROW_SIZE; i++)
		{
			for (int j=0; j<COL_SIZE; j++)
			{
				tempBoard[i][j]=board[i][j];
			}
		}
		tempPresentCol = colOfPlayer[0];

		getMove(1, presentMove);	// get the move
#ifdef DEBUG
//		printBoard();
//		cout << "\n plz enter mr : "<< colOfPlayer[0]<<"\n";
//		cin >> presentMove->x_pos;
//		cin >> presentMove->y_pos;
#endif
						//  validate the move
		if( !(isValidMove(colOfPlayer[0], presentMove->x_pos, presentMove->y_pos) >0 ))
		{				// player one lost
			scoreOne += 0 ;		//XXX useless statement
			scoreTwo += 2 ;
	
			pointsOne += 0;
			pointsTwo += count(colOfPlayer[1]);

			cout <<" \ninvalid Move\n";
			break;	
		}	

						//  update the move
		updateMove(colOfPlayer[0], presentMove->x_pos, presentMove->y_pos);
	
						// check if won or lost or draw
		if( hasMove(colOfPlayer[0]) == 0 && hasMove(colOfPlayer[1]) == 0)
		{
			int oneCount = count(colOfPlayer[0]);
			int twoCount = count(colOfPlayer[1]);
		
			if(oneCount > twoCount)
			{
				scoreOne += 2;
				scoreTwo += 0; 	//XXX useless Statement
			}	
			else if (oneCount < twoCount )
			{
				scoreOne += 0;	//XXX useless Statement
				scoreTwo += 2; 
			}
			else			// draw
			{
				scoreOne += 1;
				scoreTwo += 1;
			}

			pointsOne += oneCount;
			pointsTwo += twoCount;

			break;
		}	

						// now player two
		presentMove->x_pos = -2; 
		presentMove->y_pos = -2; 

		tempTimeLimit = timeLimit;
		
		for ( int i=0; i<ROW_SIZE; i++)
		{
			for (int j=0; j<COL_SIZE; j++)
			{
				tempBoard[i][j]=board[i][j];
			}
		}

		tempPresentCol = colOfPlayer[1];

		getMove(2, presentMove);
#ifdef DEBUG
		printBoard();
	//	cout << "\n plz enter mr : "<< colOfPlayer[1]<<"\n";
	//	cin >> presentMove->x_pos;
	//	cin >> presentMove->y_pos;
#endif
	
						// validate the move
		if( !(isValidMove(colOfPlayer[1], presentMove->x_pos, presentMove->y_pos) >0 ))
		{				// player two lost
			scoreOne += 2 ;		
			scoreTwo += 0 ;		//XXX useless statement
	
			pointsOne += count(colOfPlayer[0]);
			pointsTwo += 0;

			cout <<" \ninvalid Move\n";

			break;	
		}	

						// update the move
		updateMove(colOfPlayer[1], presentMove->x_pos, presentMove->y_pos);
	
						// check if won or lost or draw
		if( hasMove(colOfPlayer[0]) == 0 && hasMove(colOfPlayer[1]) == 0)
		{
			int oneCount = count(colOfPlayer[0]);
			int twoCount = count(colOfPlayer[1]);
		
			if(oneCount > twoCount)
			{
				scoreOne += 2;
				scoreTwo += 0; 	//XXX useless Statement
			}	
			else if (oneCount < twoCount )
			{
				scoreOne += 0;	//XXX useless Statement
				scoreTwo += 2; 
			}
			else			//draw
			{
				scoreOne += 1;
				scoreTwo += 1;
			}

			pointsOne += oneCount;
			pointsTwo += twoCount;

			break;
		}	
	}

	//print to file the stats
	ofstream myfile;
	myfile.open ("result", ios::out); 

	myfile << scoreOne<<"\n";
	myfile << pointsOne<<"\n";

	myfile << scoreTwo<<"\n";
	myfile << pointsTwo<<"\n";
}

//----------------------------------------------------
//	purpose: count points/coins belonging to a color
//	
//	input: color
//	ouput: the count
//----------------------------------------------------

int Game::count (int color)
{
	int number =0;

	for(int i=0; i<ROW_SIZE; i++)
		for(int j=0; j<COL_SIZE; j++)
			if( board[i][j] == color)
				number += 1;

	return number;
}

//----------------------------------------------------
//	purpose: change the color on the board 
//	
//	input: i,j of the move ; (inc_i, incr_j)diretion; length ; color
//	ouput: none
//----------------------------------------------------

void Game::change(int i, int j, int inc_i, int inc_j, int len, int color)
{
	int p_i = i+inc_i;
	int p_j = j+inc_j;

	while(len>0)
	{
		if(!(i>-1 && j>-1 && i<ROW_SIZE && j<COL_SIZE))
		{
			cout << "\nerror: wronge i ,j\n";
			return;
		}

		board[p_i][p_j] = color;
		len--;
		p_i = p_i +inc_i;
		p_j = p_j +inc_j;
	}

}

//----------------------------------------------------
//	purpose: update the board with the move
//	
//	input: i,j of the move ; color
//	ouput: none
//----------------------------------------------------
void Game::updateMove(int color, int i, int j)
{
	int len =0;

	int oppColor ;	// opponents color
	if(color == BLUE)
		oppColor = RED;
	else
		oppColor = BLUE;

	
	if(i>-1 && j>-1 && i<ROW_SIZE && j<COL_SIZE)
	{
		if( board[i][j] != EMPTY)
			return ;	
	
		if( (i-1)>-1 && (j-1)>-1)		// move left up	
			if( board[i-1][j-1] == oppColor)
			{
				len = lenth(i, j, -1, -1, oppColor);
				if(len>0)
					change(i, j, -1, -1, len, color);
			}	
					
		if( (i-1)>-1)				//move up
			if( board[i-1][j] == oppColor)
			{
				len = lenth(i, j, -1, 0, oppColor);
				if(len>0)
					change(i, j, -1, 0, len, color);
			}	
				
		if( (i-1)>-1 && (j+1) < COL_SIZE)	//move right up
			if( board[i-1][j+1] == oppColor)
			{
				len = lenth(i, j, -1, 1, oppColor);
				if(len >0)
					change(i, j, -1, 1, len, color);
			}		

		if( (j+1) < COL_SIZE )			//move right
			if( board[i][j+1] == oppColor)
			{
				len =lenth(i, j, 0, 1, oppColor);
				if(len >0)
					change(i, j, 0, 1, len, color);
			}			
			
		if( (i+1)<ROW_SIZE && (j+1) < COL_SIZE)	//move right down
			if( board[i+1][j+1] == oppColor)
			{
				len = lenth(i, j, 1, 1, oppColor);
				if(len >0)
					change(i, j, 1, 1, len, color);
			}	
			
		if( (i+1)<ROW_SIZE )			//move down
			if( board[i+1][j] == oppColor)
			{
				len =	lenth(i, j, 1, 0, oppColor);
				if(len >0)
					change(i, j, 1, 0, len, color);
			}	

			
		if( (i+1)< ROW_SIZE && (j-1) > -1 )	//move left down
			if( board[i+1][j-1] == oppColor)
			{
				len =lenth(i, j, 1, -1, oppColor);
				if(len >0)
					change(i, j, 1, -1, len, color);
			}		

		if( (j-1) > -1)				//move left 
			if( board[i][j-1] == oppColor )
			{
				len = lenth(i, j, 0, -1, oppColor);
				if(len >0)
					change(i, j, 0, -1, len, color);
			}		

		
		board[i][j] = color;
		return ;
	}	
	else
		return ;

}	

//----------------------------------------------------
//	purpose: check the move
//	
//	input: 	i,j of the move ; color
//	ouput: 	0 	if not valid 
//		>0	if valid move
//----------------------------------------------------

int Game::isValidMove(int color, int i, int j)
{
	int hasmove = hasMove(color);
	int len = 0;

	int oppColor ;	// opponents color
	if(color == BLUE)
		oppColor = RED;
	else
		oppColor = BLUE;

	if( i == -2 && j == -2)
		return 0;

	if (hasmove == 0)		// if there is no move 
	{				// player should return (-1,-1)
		if( i == -1 && j == -1) // else they lose the game
			return 1;
		else 
			return 0;
	}	
	else 
	{
		if(i>-1 && j>-1 && i<ROW_SIZE && j<COL_SIZE)
		{
			if( board[i][j] != EMPTY)
				return 0;	
	
			if( (i-1)>-1 && (j-1)>-1)		// move left up	
				if( board[i-1][j-1] == oppColor)
					len += lenth(i, j, -1, -1, oppColor);
					
			if( (i-1)>-1)				//move up
				if( board[i-1][j] == oppColor)
					len += lenth(i, j, -1, 0, oppColor);
				
			if( (i-1)>-1 && (j+1) < COL_SIZE)	//move right up
				if( board[i-1][j+1] == oppColor)
					len += lenth(i, j, -1, 1, oppColor);

			if( (j+1) < COL_SIZE )			//move right
				if( board[i][j+1] == oppColor)
						len +=lenth(i, j, 0, 1, oppColor);
			
			if( (i+1)<ROW_SIZE && (j+1) < COL_SIZE)	//move right down
				if( board[i+1][j+1] == oppColor)
					len += lenth(i, j, 1, 1, oppColor);
				
			if( (i+1)<ROW_SIZE )			//move down
				if( board[i+1][j] == oppColor)
					len +=	lenth(i, j, 1, 0, oppColor);
				
			if( (i+1)<ROW_SIZE && (j-1) > -1)	//move left down
				if( board[i+1][j-1] == oppColor)
					len +=lenth(i, j, 1, -1, oppColor);

			if( (j-1) >-1)				//move left 
				if( board[i][j-1] == oppColor )
					len += lenth(i, j, 0, -1, oppColor);	

			return len;
		}	
		else
			return 0;	
	}	
}

//----------------------------------------------------
//	purpose: check if move exist for a color
//	
//	input: color
//	ouput: 	0 if no move 
//		1 if exists
//----------------------------------------------------
int Game::hasMove(int color)
{
	int oppColor ;	// opponents color

	if(color == BLUE)
		oppColor = RED;
	else
		oppColor = BLUE;
	
	for(int i=0; i<ROW_SIZE; i++)
	{
		for(int j=0; j<COL_SIZE; j++)
		{
			if(board[i][j] != EMPTY)			//should be empty
				continue;

			else
			{
				if( (i-1)>-1 && (j-1)>-1)		// move left up	
					if( board[i-1][j-1] == oppColor && lenth(i, j, -1, -1, oppColor) >0)
						return 1;
					
				if( (i-1)>-1 )				//move up
					if( board[i-1][j] == oppColor && lenth(i, j, -1, 0, oppColor) >0)
						return 1;
				
				if( (i-1)>-1 && (j+1) < COL_SIZE)	//move right up
					if( board[i-1][j+1] == oppColor && lenth(i, j, -1, 1, oppColor) >0)
						return 1;

				if( (j+1) < COL_SIZE )			//move right
					if( board[i][j+1] == oppColor && lenth(i, j, 0, 1, oppColor) >0)
						return 1;
			
				if( (i+1)<ROW_SIZE && (j+1) < COL_SIZE)	//move right down
					if( board[i+1][j+1] == oppColor && lenth(i, j, 1, 1, oppColor) >0)
						return 1;
				
				if( (i+1)<ROW_SIZE )			//move down
					if( board[i+1][j] == oppColor && lenth(i, j, 1, 0, oppColor) >0)
						return 1;
				
				if( (i+1)<ROW_SIZE && (j-1) > -1)	//move left down
					if( board[i+1][j-1] == oppColor && lenth(i, j, 1, -1, oppColor) >0)
						return 1;

				if( (j-1) > -1)				//move left 
					if( board[i][j-1] == oppColor && lenth(i, j, 0, -1, oppColor) >0)
						return 1;
			}
		}
	}
	return 0;
}

//----------------------------------------------------
//	purpose: length of change along that direction
//	
//	input: i,j of the move ; (inc_i, incr_j)diretion; length ; opponents color
//	ouput: length
//----------------------------------------------------

int Game::lenth(int i, int j, int inc_i, int inc_j, int oppColor)
{
	int myColor;
	int length = 0;
	
	if(oppColor == BLUE)
		myColor = RED;
	else
		myColor = BLUE;
	
	i = i + inc_i;
	j = j + inc_j;

	while(1)
	{
		if(i>-1 && j>-1 && i<ROW_SIZE && j<COL_SIZE)
		{
			if(board[i][j]==oppColor)
			{
				length++;
				
				i += inc_i;
				j += inc_j;
			}	
		
			else if(board[i][j]==EMPTY)
				return 0;

			else
				return length;
		}		
		else
			return 0;
	
	}
	return 0;
}

//----------------------------------------------------
//	purpose: create player thread and timer thread
//	
//	input: player ; move
//	ouput: none
//----------------------------------------------------

void Game::getMove(int player, struct move *givenMove )
{
  	pthread_t  thread;
	done = 0;

	struct move * pMove;
	pMove = (move *) malloc (sizeof (struct move));

	pMove->x_pos = -2; 
	pMove->y_pos = -2; 
	
	long long int ticks = clock();		//start timer
	ticks = ticks*1000/CLOCKS_PER_SEC;

	if(player == 1)				//create player thread
		pthread_create(&thread, NULL, playerOneMove, (void*) pMove);
	else
		pthread_create(&thread, NULL, playerTwoMove, (void*) pMove);
		
	

#ifdef DEBUG
	cout<<"\n thread created \n";
#endif	
	while(1)		// thread to detect if player is in infinite loop 
	{
		long long int sec = clock();
		sec = sec*1000/CLOCKS_PER_SEC - ticks; 
			
#ifdef DEBUG
//			cout <<"\n done :"<< done;
#endif	
		if(sec > (2*timeLimit))	// timer = 2* timit limit 	
		{
	//		pthread_cancel(thread);

			cout <<"\n thread cancelled : probably in infinite loop\n";
			
			givenMove->x_pos = -2;
			givenMove->y_pos = -2;
			
			return;

		}
		if(done == 1)	// if player returned return move
		{
#ifdef DEBUG
			cout <<"\n finished \n";
#endif	
			givenMove->x_pos = pMove->x_pos;
			givenMove->y_pos = pMove->y_pos;
			return;
		}
	
	}
 }

//----------------------------------------------------
//	purpose:  call player ones run() function and check time 
//	
//	input: move
//	ouput: none
//----------------------------------------------------
void * playerOneMove(void * myMove)
{
	long long int ticks = clock();
	ticks = ticks*1000/CLOCKS_PER_SEC;	// timer start

	struct move * myM;
	myM = (move *) malloc (sizeof (struct move));

	done = 0;
	G_1 gOne ;	//player one class

//	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
//	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
			// call his run function
	gOne.run(tempBoard, tempTimeLimit, tempPresentCol, myM); 

#ifdef DEBUG
	cout <<"\n "<<myM->x_pos<<" ,"<< myM->y_pos<< "\n";
#endif	
	

	long long int finalTicks = clock();
	finalTicks = finalTicks*1000/CLOCKS_PER_SEC;
	finalTicks = finalTicks - ticks;	// timer end

	done =0;
						// if u cross time limit move is not updated
	if(finalTicks < (1.1*tempTimeLimit))		// 10% grace time
	{
		((move*)myMove)->x_pos = myM->x_pos;
		((move*)myMove)->y_pos = myM->y_pos;
		done = 1;
	}
}

//----------------------------------------------------
//	purpose:  call player ones run() function and check time 
//	
//	input: move
//	ouput: none
//----------------------------------------------------
void * playerTwoMove(void * myMove)
{
	long long int ticks = clock();
	ticks = ticks*1000/CLOCKS_PER_SEC;
	
	struct move * myM;
	myM = (move *) malloc (sizeof (struct move));
	
	done = 0;
	G_2 gTwo ;				//player two class

//	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
//	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

						// call his run function
	gTwo.run(tempBoard, tempTimeLimit, tempPresentCol, myM); 
#ifdef DEBUG
	cout <<"\n "<<myM->x_pos<<" ,"<< myM->y_pos<< "\n";
#endif	
	
	long long int finalTicks = clock();
	finalTicks = finalTicks*1000/CLOCKS_PER_SEC;
	finalTicks = finalTicks - ticks; 
	
	done = 0;	
						// if u cross time limit move is not updated
	if(finalTicks < (1.1*tempTimeLimit))		// 10% grace time
	{
		((move*)myMove)->x_pos = myM->x_pos;
		((move*)myMove)->y_pos = myM->y_pos;
		done = 1;
	}
}
